import * as effectTypes from './effectTypes';
import {TASK_CANCEL} from './symbols';

function times(cb,length){
  const result = [];
  let count = 0;
  return function(res){
    result[count] = res;
    if(++count === length){
      cb(result);
    }
  }
}

/**
 * 执行 或则 启动saga的方法
 *
 * @param saga
 */
function runSaga(
  env/*←middlewareAPI+channel*/,
  saga/*← 可能是一个生成器,也可能是一个迭代器(比如在rootSaga里 yield了另外一个生成器的迭代器)*/,
  doneCallback
) {
  const subTasks = [];

  const {getState, dispatch, channel} = env;
  //↑ 为什么需要env 因为我们在执行saga中 可能会获取状态 也可能是派发动作 也可以能是监听动作

  const task = {cancel: () => next(TASK_CANCEL)}; //← 每个runSaga(即每个迭代器)都会对应有一个这个task 作为返回值, 用于取消中断本迭代器执行

  const it = typeof saga === 'function' ? saga()/*生成器,需要生成迭代器*/ : saga/*已经是个迭代器*/;

  next();

  function next(value,isError) {
    let result;
    if (isError) {
      result = it.throw(value);
    } else if (value === TASK_CANCEL) { //说明要取消任务
      subTasks.forEach(t => t.cancel()); //←父saga取消, 要把它所有子saga也取消
      result = it.return(value);
    } else {
      result = it.next(value); //effect 就是我们 effects.js 里那些返回的对象
    }
    const {value: effect, done} = result;

    if (!done)  {

      // 如果 yield的是一个 iterator, 则递归执行runSaga
      if(typeof effect[Symbol.iterator]==='function'){
        runSaga(env, effect); //让它自己单独执行去
        next(); //不会阻塞当前的saga继续next执行

      }else if (typeof effect.then === 'function'){ //是个promise
        effect.then(next);

      }else {
        switch (effect.type/*←这个type是effect的type*/) {
        case effectTypes.TAKE: //我想等待某人向仓库派发XXX类型的动作
          channel.take(effect.actionType/*←这个type,是向仓库派发的动作的类型*/, next)
          break
        case effectTypes.PUT:
          dispatch(effect.action);
          next();
          break
        case effectTypes.FORK:
          const forkTask = runSaga(env, effect.saga); //开启一个新的子进程单独执行
          //↑不会阻塞当前的saga继续执行↓

          subTasks.push(forkTask);

          next(forkTask); //← next(value)的value 会被当做下一次 it.next(value)接受的value, 也就说会作为下一次迭代的输入, 这样生成器里我们就能拿到这个可能会取消的task了
          break
        case effectTypes.CALL:
          effect.fn(...effect.args).then(next, (err) => next(err, true));
          break
        case effectTypes.CPS:
          effect.fn(...effect.args,(err,data)=>{
            if(err){
              next(err, true);
            }else {
              next(data);
            }
          })
          break
        case effectTypes.ALL:
          const effects = effect.effects;

          /*const result = [];
          let completedCount = 0;
          effects.forEach((effect,index)=>{
            runSaga(env,effect,(res/!*一个生成器最后一次迭代的返回值*!/)=>{
              result[index] = res;
              if (++completedCount === effects.length) {
                next(result);
              }
            })
          })*/

          const done = times(next, effects.length);
          effects.forEach(effect=>{
            const subTask = runSaga(env, effect, done);
            subTasks.push(subTask);
          });

          break;
        case effectTypes.CANCEL:
          effect.task.cancel();
          next();
          break;
        case effectTypes.SELECT:
          const state = effect.selector(getState())
          next(state); // const state = yield select(state=>state.counter1)
          break;
        default:
          break;
        }
      }

    }else {
      doneCallback && doneCallback(effect/*一个生成器最后一次迭代的返回值*/);
    }
  }

  return task;
}

export default runSaga;
